import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { PerspectiveCamera, Mesh, Scene, Vector2, Color, WebGLRenderer, AmbientLight, Group, Object3D, Raycaster, Vector3, Material, BufferGeometry, MeshBasicMaterial, BoxGeometry, DoubleSide } from 'three';
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";


import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import Frame from './model/frame.js';
import FrameJSON from './model/frameJSON.js';
import Platform from './model/platform.js';
import { DataLine } from './model/dataLine.js';
import { Device } from './model/device.js';
// import ModelConnection from './model/modelConnection.js';
import { Plane } from './model/plane.js';

// import { Line2 } from 'three/addons/lines/Line2.js';
// import { LineGeometry } from 'three/addons/lines/LineGeometry.js';
// import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
// import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';

import { AnimationManager } from "./lib/AnimationManager.js";
import * as THREE from "three";
import * as TWEEN from "@tweenjs/tween.js";
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
import { Line2 } from 'three/examples/jsm/lines/Line2.js';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
import Settings from './model/settings.js';
import { SETTINGS } from './model/WORDS.js';

let pointer: Vector2, raycaster: Raycaster

class View {
    scene = new Scene();
    camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000)
    renderer = new WebGLRenderer({ antialias: true, alpha: true })
    controls = new OrbitControls(this.camera, this.renderer.domElement)

    // 边界发光
    _outlinePass: OutlinePass | null;
    _outlinePassS: OutlinePass | null;
    _composer: EffectComposer | null;
    _renderPass: RenderPass | null
    _lightedObjs = new Map<string, Object3D>()

    frame: Frame | null
    _animationManager: AnimationManager | null
    settings: Settings | undefined

    constructor() {
        const me = this
        const scene = me.scene
        const camera = me.camera
        const renderer = me.renderer

        const texture = new THREE.TextureLoader().load(
            "./textures/bg.png",
            // function (tex) {
            // 	tex.needsUpdate = true;
            // 	tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
            // 	tex.repeat.set(2, 2);
            // }
        )
        // texture.image
        scene.background = texture
        // scene.background = new THREE.Color('black')

        camera.position.set(250, 400, 650)
        camera.lookAt(0, 0, 0)

        // renderer.setClearColor(0x000000)
        // renderer.setClearAlpha(0.2)
        renderer.setPixelRatio(window.devicePixelRatio)
        renderer.setSize(window.innerWidth, window.innerHeight)

        // 灯光
        const ambientLight = new AmbientLight(0xffffff, 0); // 环境光
        scene.add(ambientLight)
        document.body.appendChild(me.renderer.domElement)

        this.frame = null
        raycaster = new Raycaster()
        pointer = new Vector2()


        window.addEventListener('resize', this._onWindowResize.bind(this))


        this._composer = new EffectComposer(this.renderer)
        this._renderPass = new RenderPass(this.scene, this.camera)
        // this._outlinePass = new OutlinePass(new Vector2(window.innerWidth, window.innerHeight), this.scene, this.camera)
        this._composer.addPass(this._renderPass);


        // 包含 EdgeShader 和 LineBasicMaterial 的着色器程序
        const edgeVertexShader = [
            'uniform float offset;',
            'void main() {',
            '  vec4 pos = vec4( modelViewMatrix * vec4( position + normal * offset, 1.0 ) );',
            '  gl_Position = projectionMatrix * pos;',
            '}',
        ].join('\n');

        const visibleColor = new THREE.Color('#248dab'); // 红色

        this._outlinePass = new OutlinePass(new Vector2(window.innerWidth, window.innerHeight), scene, camera)
        this._outlinePass.edgeStrength = 3; // 辉光强度
        this._outlinePass.edgeGlow = 1; // 辉光大小
        this._outlinePass.edgeThickness = 1; // 辉光厚度
        this._outlinePass.pulsePeriod = 0; // 周期性变化效果（设置为0表示没有周期性变化）
        const hiddenColor = new Color(1, 0, 0); // 绿色
        this._outlinePass.visibleEdgeColor.copy(visibleColor);
        this._outlinePass.hiddenEdgeColor.copy(visibleColor);
        this._composer.addPass(this._outlinePass);

        // 选中发光
        this._outlinePassS = new OutlinePass(new Vector2(window.innerWidth, window.innerHeight), scene, camera)
        this._outlinePassS.edgeStrength = 5; // 辉光强度
        this._outlinePassS.edgeGlow = 1; // 辉光大小
        this._outlinePassS.edgeThickness = 1; // 辉光厚度
        this._outlinePassS.pulsePeriod = 0; // 周期性变化效果（设置为0表示没有周期性变化）
        // const hiddenColor = new THREE.Color(0, 1, 0); // 绿色
        this._outlinePassS.visibleEdgeColor.copy(visibleColor);
        this._outlinePassS.hiddenEdgeColor.copy(visibleColor);
        this._composer.addPass(this._outlinePassS);




        this._composer.addPass(new OutputPass())

        // 抗锯齿
        let fxaaPass = new ShaderPass(FXAAShader)
        const pixelRatio = this.renderer.getPixelRatio()
        fxaaPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth * pixelRatio)
        fxaaPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight * pixelRatio)

        this._composer.addPass(fxaaPass);

        document.addEventListener('dblclick', this._dblclickWhenAddModelConnection.bind(this))


        //生成线路动画
        this._animationManager = new AnimationManager(me.scene); //创建动画管理类

        me.resetScene()
        me._render()

    }

    resetScene() {

    }

    /**
     * 
     * @param frameUrl 
     * @param frameTreeUrl
     * 
     */
    load(frameTreeUrl: string, settingsUrl?: string) {
        const me = this
        const scene = me.scene

        if (!frameTreeUrl) {
            return
        }
        fetch(frameTreeUrl).then(res => res.json()).then(res => {
            const frameJSON = res as FrameJSON
            this.frame = new Frame()
            this.frame.offY = frameJSON.offY
            frameJSON.platforms.forEach(i => {
                const faceTex = new THREE.TextureLoader().load(
                    i.plane.textureUrl,
                    // function (tex) {
                    // 	tex.needsUpdate = true;
                    // 	tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
                    // 	tex.repeat.set(2, 2);
                    // }
                );
                const planeMesh = new Mesh<BoxGeometry, MeshBasicMaterial>(new BoxGeometry(i.plane.width, SETTINGS.PLANE.HEIGHT, i.plane.depth), new MeshBasicMaterial({
                    side: DoubleSide,
                    color: i.plane.color,
                    map: faceTex,
                    opacity: i.plane.opacity,
                    transparent: true
                }))
                me._outlinePass?.selectedObjects.push(planeMesh)
                const platform = new Platform(i.index, new Plane(planeMesh, i.plane))
                scene.add(platform.plane.obj)
                i.dataLines.map(i => {
                    if (i.id) {
                        // 根据params.points生成line(Line2)
                        const linegeo = new LineGeometry()
                        linegeo.setPositions(i.points.flat())
                        const dataLineObj = new Line2(linegeo, new LineMaterial({
                            color: i.color,
                            worldUnits: true,
                            linewidth: i.width,
                        }))
                        // dataLineObj.userData.attr = i.attr
                        const dataLine = new DataLine(dataLineObj, i)
                        scene.add(dataLine.obj)
                        platform.dataLines.set(i.id, dataLine)
                    }
                })
                i.devices.map(i => {
                    if (i.id) {
                        // 根据params生成box
                        const geo = new BoxGeometry(i.width, i.height, i.depth)
                        geo.translate(0, i.height / 2, 0)
                        const mtl = new MeshBasicMaterial({
                            color: i.color,
                            opacity: i.opacity,
                            transparent: true,
                        })
                        const mesh = new Mesh<BoxGeometry, MeshBasicMaterial>(geo, mtl)
                        // 位置

                        mesh.position.set(i.position[0], i.position[1], i.position[2])
                        const device = new Device(mesh, i)
                        scene.add(device.obj)
                        platform.devices.set(i.id, device)
                    }
                })
                platform.updateOffY(i.index, frameJSON.offY)
                this.frame?.platforms.set(platform.name, platform)
            })




            settingsUrl && fetch(settingsUrl).then(res => res.json()).then(res => {
                this.settings = res
            })




        })



    }


    // 事件
    private _dblclickWhenAddModelConnection(event: MouseEvent) {
        if (!this.settings) {
            return
        }
        const me = this

        pointer.set((event.clientX / window.innerWidth) * 2 - 1, - (event.clientY / window.innerHeight) * 2 + 1);

        raycaster.setFromCamera(pointer, me.camera);

        let deviceObjs: Mesh[] = []
        me.frame?.platforms.forEach(p => {
            p.devices.forEach(d => {
                deviceObjs.push(d.obj)
            });
        })
        const intersects = raycaster.intersectObjects(deviceObjs, true);

        if (intersects.length > 0) {
            const intersect = intersects[0]
            if (intersect) {
                const device = intersect.object
                console.log(device.name);

                const cons = this.settings.connections.filter(i => { return i.deviceA === device.name })
                if (cons.length > 0) {
                    const devices = new Map<string, Object3D>()
                    cons.forEach(con => {
                        const deviceA = this.scene.getObjectByName(con.deviceA) as Mesh<BoxGeometry, MeshBasicMaterial>
                        deviceA.material.color = new THREE.Color('#248dab')
                        deviceA.material.opacity = 1
                        const deviceB = this.scene.getObjectByName(con.deviceB) as Mesh<BoxGeometry, MeshBasicMaterial>
                        deviceB.material.color = new THREE.Color('#248dab')
                        deviceB.material.opacity = 1
                        if (deviceA && deviceB) {
                            me._connectionLine(deviceA.position, deviceB.position)
                            devices.set(deviceA.name, deviceA)
                            devices.set(deviceB.name, deviceB)
                        }
                    })

                    me._outlinePassS!.selectedObjects = Array.from(devices.values())



                }
            }
        } else {
            // me._outlinePass!.selectedObjects = []
        }
    }

    private _connectionLine(pos1: Vector3, pos2: Vector3) {
        const me = this
        //生成线路动画
        const animationManager = me._animationManager! //创建动画管理类
        const pointArr1 = [pos1, pos2];


        const genTween1 = animationManager.generateLine(
            "line1",
            [pos1, pos2],
            1,
            new THREE.Color(0x26c7fd),
            1
        );

        // //线路上流光动画
        // const curve1 = new THREE.CatmullRomCurve3(pointArr1);
        // const flyLinePoints = curve1.getSpacedPoints(100);
        // const flyLineTex = new THREE.TextureLoader().load(
        //     "./textures/flow.png",
        //     function (tex) {
        //         tex.needsUpdate = true;
        //         tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
        //         tex.repeat.set(1, 1);
        //     }
        // );
        // const flyLineTween = animationManager.createMovedLine(
        //     "flyline1",
        //     flyLinePoints,
        //     0.1,
        //     10,
        //     0.5,
        //     new THREE.Color(0xffffff),
        //     flyLineTex
        // );

        // genTween1.chain(flyLineTween);
        // genTween2.chain(genTween4)
        // flyLineTween.start()
        genTween1.start();
    }


    private conLine(pos1: Vector3, pos2: Vector3) {
        const me = this
        // const geo = new LineGeometry()
        // geo.setPositions([pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z])
        // const line2 = new Line2(geo, new LineMaterial({
        //     color: 'red',
        //     worldUnits: true,
        //     linewidth: 1
        // }))
        // this.scene.add(line2)
        // 创建线条的几何形状
        var geometry = new THREE.BufferGeometry();
        var positions = []; // 存储线条的顶点坐标

        // 添加起点坐标
        positions.push(pos1.x, pos1.y, pos1.z); // 起点坐标
        // positions.push(pos2.x, pos2.y, pos2.z); // 终点坐标

        geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));

        // 创建线条的材质
        var material = new THREE.LineBasicMaterial({ color: 'red' });

        // 创建线条对象
        var line = new THREE.Line(geometry, material);
        this.scene.add(line)

        // 定义动画参数
        var animationDuration = 2000; // 动画持续时间（毫秒）

        // 创建动画
        var tween = new TWEEN.Tween(line.geometry.position)
            .to({ x: pos2.x, y: pos2.y, z: pos2.z }, animationDuration)
            .easing(TWEEN.Easing.Linear.None)
            .onUpdate(function () {
                // 更新几何形状的属性
                geometry.attributes.position.needsUpdate = true;

                // 渲染场景
                me.renderer.render(me.scene, me.camera);
            });
        tween.start()
    }

    private _onWindowResize() {
        const me = this
        me.camera.aspect = window.innerWidth / window.innerHeight;
        me.camera.updateProjectionMatrix();
        me.renderer.setSize(window.innerWidth, window.innerHeight)
        me._composer?.setSize(window.innerWidth, window.innerHeight)
    }

    _merge(group: Group) {
        // 创建一个新的 Mesh
        var mergedMesh = new Mesh();
        const materials: Material[] = []
        const geos: BufferGeometry[] = []

        // 遍历模型的子对象
        group.traverse(function (child) {
            if (Object.prototype.hasOwnProperty.call(child, "isMesh")) {
                // 合并子对象为一个整体的 Mesh
                const childMesh = (child as Mesh)
                // let matrixWorldGeometry = childMesh.geometry.clone().applyMatrix4(childMesh.matrixWorld);
                //这个时候 matrixWorldGeometry 几何体的位置就已经转化成世界坐标的位置了

                geos.push(childMesh.geometry.clone())
                // mergedMesh.geometry.merge(childMesh.geometry, childMesh.matrix);
                materials.push(childMesh.material as Material)
            }
        })

        let merged = mergeGeometries(geos, true)

        mergedMesh.geometry = merged
        mergedMesh.material = materials

        mergedMesh.scale.copy(group.scale)
        // 针对测试的glb，对gltf.scene子节点group，进行同样的旋转位置调整
        mergedMesh.rotation.copy(group.rotation)

        return mergedMesh
    }

    _render() {
        const me = this
        me.controls.update();
        TWEEN.update();
        me._composer?.render();
        requestAnimationFrame(me._render.bind(me));
        // me.renderer.render(me.scene, me.camera);
        // 边界发光

    }

}

let viewC = new View()
viewC.load("./data/frameTree.json", "./data/settings.json")

window["viewC"] = viewC
